#include "stdafx.h"
#include "Md2.h"
#include "../tinyxml/tinyxml.h"

CMD2::CMD2()
{
	memset(&m_Header, 0, sizeof(tMd2Header));
	m_pSkins=NULL;
	m_pTexCoords=NULL;
	m_pTriangles=NULL;
	m_pFrames=NULL;
}

CMD2::~CMD2()
{
	// Go through all the objects in the scene
	for(int i = 0; i < m_pModel->numOfObjects; i++)
	{
		// Free the faces, normals, vertices, and texture coordinates.
		if(m_pModel->pObject[i].pFaces)		delete [] m_pModel->pObject[i].pFaces;
		if(m_pModel->pObject[i].pNormals)	delete [] m_pModel->pObject[i].pNormals;
		if(m_pModel->pObject[i].pVerts)		delete [] m_pModel->pObject[i].pVerts;
		if(m_pModel->pObject[i].pTexVerts)	delete [] m_pModel->pObject[i].pTexVerts;
	}
}

void CMD2::CreateTexture(LPCWSTR strFileName, int textureID)
{
	AUX_RGBImageRec *pBitmap = NULL;

	if(!strFileName)									// Return from the function if no file name was passed in
		return;

	pBitmap = auxDIBImageLoad(strFileName);				// Load the bitmap and store the data

	if(pBitmap == NULL)									// If we can't load the file, quit!
		exit(0);

	// Generate a texture with the associative texture ID stored in the array
	glGenTextures(1, &m_pTexture[textureID]);

	// This sets the alignment requirements for the start of each pixel row in memory.
	glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

	// Bind the texture to the texture arrays index and init the texture
	glBindTexture(GL_TEXTURE_2D, m_pTexture[textureID]);

	// Build Mipmaps (builds different versions of the picture for distances - looks better)
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data);

	// Lastly, we need to tell OpenGL the quality of our texture map.  GL_LINEAR_MIPMAP_LINEAR
	// is the smoothest.  GL_LINEAR_MIPMAP_NEAREST is faster than GL_LINEAR_MIPMAP_LINEAR, 
	// but looks blochy and pixilated.  Good for slower computers though.  

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);

	// Now we need to free the bitmap data that we loaded since openGL stored it as a texture

	if (pBitmap)										// If we loaded the bitmap
	{
		if (pBitmap->data)								// If there is texture data
		{
			free(pBitmap->data);						// Free the texture data, we don't need it anymore
		}

		free(pBitmap);									// Free the bitmap structure
	}
}

void CMD2::CleanUp()
{
	fclose(m_FilePointer);						// Close the current file pointer
	if(m_pSkins)	 delete [] m_pSkins;		// Free the skins data
	if(m_pTexCoords) delete m_pTexCoords;		// Free the texture coord data
	if(m_pTriangles) delete m_pTriangles;		// Free the triangle face data
	if(m_pFrames)	 delete m_pFrames;			// Free the frames of animation
}


bool CMD2::ImportMD2(const wchar_t *strFileName, const wchar_t *strTexture)
{
	char strMessage[255] = {0};

	// Open the MD2 file in binary
	m_FilePointer = _wfopen(strFileName, _T("rb"));

	// Make sure we have a valid file pointer (we found the file)
	if(!m_FilePointer) 
	{
		assert(0);
		return false;
	}

	m_pModel = new t3DModel;
	
	// Read the header data and store it in our m_Header member variable
	fread(&m_Header, 1, sizeof(tMd2Header), m_FilePointer);

	// Make sure the version is this crazy number '8' or else it's a bad egg
	if(m_Header.version != 8)
	{
		assert(0);
		return false;
	}

	// Read in the model and animation data
	ReadMD2Data();
	
	// Here we pass in our model structure to it can store the read Quake data
	// in our own model and object structure data
	ConvertDataStructures();

	// If there is a valid texture name passed in, we want to set the texture data
	if(strTexture)
	{
		// Create a local material info structure
		tMaterialInfo texture;

		// Copy the name of the file into our texture file name variable
		wcscpy(texture.strFile, strTexture);

		// Since there is only one texture for a .Md2 file, the ID is always 0
		texture.texureId = 0;

		// The tile or scale for the UV's is 1 to 1 (but Quake saves off a 0-256 ratio)
		texture.uTile = texture.uTile = 1;

		// We only have 1 material for a model
		m_pModel->numOfMaterials = 1;

		// Add the local material info to our model's material list
		m_pModel->pMaterials.push_back(texture);
	}

	CleanUp();

	m_pTexture = new GLuint[m_pModel->numOfMaterials];
	// Go through all the materials
	for(int i = 0; i < m_pModel->numOfMaterials; i++)
	{
		// Check to see if there is a file name to load in this material
		if(wcslen(m_pModel->pMaterials[i].strFile) > 0)
		{
			// Use the name of the texture file to load the bitmap, with a texture ID (i).
			// We pass in our global texture array, the name of the texture, and an ID to reference it.	
			CreateTexture(m_pModel->pMaterials[i].strFile, i);			
		}

		// Set the texture ID for this material
		m_pModel->pMaterials[i].texureId = i;
	}
	return true;
}

void CMD2::ReadMD2Data()
{
	// Create a larger buffer for the frames of animation (not fully used yet)
	unsigned char buffer[MD2_MAX_FRAMESIZE];

	// Here we allocate all of our memory from the header's information
	m_pSkins     = new tMd2Skin [m_Header.numSkins];
	m_pTexCoords = new tMd2TexCoord [m_Header.numTexCoords];
	m_pTriangles = new tMd2Face [m_Header.numTriangles];
	m_pFrames    = new tMd2Frame [m_Header.numFrames];

	// Next, we start reading in the data by seeking to our skin names offset
	fseek(m_FilePointer, m_Header.offsetSkins, SEEK_SET);
	
	// Depending on the skin count, we read in each skin for this model
	fread(m_pSkins, sizeof(tMd2Skin), m_Header.numSkins, m_FilePointer);
	
	// Move the file pointer to the position in the file for texture coordinates
	fseek(m_FilePointer, m_Header.offsetTexCoords, SEEK_SET);
	
	// Read in all the texture coordinates in one fell swoop
	fread(m_pTexCoords, sizeof(tMd2TexCoord), m_Header.numTexCoords, m_FilePointer);

	// Move the file pointer to the triangles/face data offset
	fseek(m_FilePointer, m_Header.offsetTriangles, SEEK_SET);
	
	// Read in the face data for each triangle (vertex and texCoord indices)
	fread(m_pTriangles, sizeof(tMd2Face), m_Header.numTriangles, m_FilePointer);
			
	// Move the file pointer to the vertices (frames)
	fseek(m_FilePointer, m_Header.offsetFrames, SEEK_SET);

	// In the last tutorial we just read in one frame of animation here.  Now we are going
	// to extract every key frame from the .MD2 model.  These key frames will be used to
	// interpolate between each other to form the somewhat smooth animation.
	// The only thing different is that we are putting a for loop in front, then changing
	// the '0' for the m_pFrames[0] in the last tutorial, to a 'i'.  The loop will
	// continue until 'i' has reached the number of key frames for this model.

	for (int i=0; i < m_Header.numFrames; i++)
	{
		// Assign our alias frame to our buffer memory
		tMd2AliasFrame *pFrame = (tMd2AliasFrame *) buffer;

		// Allocate the memory for the first frame of animation's vertices
		m_pFrames[i].pVertices = new tMd2Triangle [m_Header.numVertices];

		// Read in the first frame of animation
		fread(pFrame, 1, m_Header.frameSize, m_FilePointer);

		// Copy the name of the animation to our frames array
		strcpy(m_pFrames[i].strName, pFrame->name);
			
		// Store off a vertex array pointer to cut down large lines of code
		tMd2Triangle *pVertices = m_pFrames[i].pVertices;

		// Go through all of the number of vertices and assign the scale and translations.
		// Store the vertices in our current frame's vertex list array, while swapping Y and Z.
		// Notice we also negate the Z axis as well to make the swap correctly.
		for (int j=0; j < m_Header.numVertices; j++)
		{
			pVertices[j].vertex[0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
			pVertices[j].vertex[2] = -1 * (pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1]);
			pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
		}
	}
}

void CMD2::ParseAnimations()
{
	tAnimationInfo animation;
	string strLastName = "";

	// Go through all of the frames of animation and parse each animation
	for(int i = 0; i < m_pModel->numOfObjects; i++)
	{
		// Assign the name of this frame of animation to a string object
		string strName  = m_pFrames[i].strName;
		int frameNum = 0;
		
		// Go through and extract the frame numbers and erase them from the name
		for(unsigned int j = 0; j < strName.length(); j++)
		{
			// If the current index is a number and it's one of the last 2 characters of the name
			if( isdigit(strName[j]) && j >= strName.length() - 2)
			{
				// Use a C function to convert the character to a integer.
				// Notice we use the address to pass in the current character and on
				frameNum = atoi(&strName[j]);

				// Erase the frame number from the name so we extract the animation name
				strName.erase(j, strName.length() - j);
				break;
			}
		}

		// Check if this animation name is not the same as the last frame,
		// or if we are on the last frame of animation for this model
		if(strName != strLastName || i == m_pModel->numOfObjects - 1)
		{
			// If this animation frame is NOT the first frame
			if(strLastName != "")
			{
				// Copy the last animation name into our new animation's name
				strcpy(animation.strName, strLastName.c_str());

				// Set the last frame of this animation to i
				animation.endFrame = i;

				// Add the animation to our list and reset the animation object for next time
				m_pModel->pAnimations.push_back(animation);
				memset(&animation, 0, sizeof(tAnimationInfo));

				// Increase the number of animations for this model
				m_pModel->numOfAnimations++;
			}

			// Set the starting frame number to the current frame number we just found,
			// minus 1 (since 0 is the first frame) and add 'i'.
			animation.startFrame = frameNum - 1 + i;
		}

		// Store the current animation name in the strLastName string to check it latter
		strLastName = strName;
	}
}

void CMD2::ConvertDataStructures()
{
	int j = 0, i = 0;

	// Initialize the model structure first before assigning data to it
	memset(m_pModel, 0, sizeof(t3DModel));
	
	// Set the number of objects to our number of frames since pObjects will
	// hold the key frames
	m_pModel->numOfObjects = m_Header.numFrames;

	// Create our animation list and store it in our model
	ParseAnimations();

	// Go through every key frame and store it's vertices info in our pObject list.
	for (i=0; i < m_pModel->numOfObjects; i++)
	{
		// Create a local object to store the first frame of animation's data
		t3DObject currentFrame = {0};

		// Assign the vertex, texture coord and face count to our new structure
		currentFrame.numOfVerts   = m_Header.numVertices;
		currentFrame.numTexVertex = m_Header.numTexCoords;
		currentFrame.numOfFaces   = m_Header.numTriangles;

		// Allocate memory for the vertices, texture coordinates and face data.
		currentFrame.pVerts    = new CV3 [currentFrame.numOfVerts];

		// Go through all of the vertices and assign them over to our structure
		for (j=0; j < currentFrame.numOfVerts; j++)
		{
			currentFrame.pVerts[j].x = m_pFrames[i].pVertices[j].vertex[0];
			currentFrame.pVerts[j].y = m_pFrames[i].pVertices[j].vertex[1];
			currentFrame.pVerts[j].z = m_pFrames[i].pVertices[j].vertex[2];
		}

		// We can now free the old vertices stored in this frame of animation
		// since we have them stored in the pObject list of our model
		delete m_pFrames[i].pVertices;
	
		// Now comes the tricky part, since we only need to store the UV coordinates
		// and face indices once, we only do this on the first frame.  If the
		// current frame is past the first frame, we just add the current frame
		// as it is to our list, then continue to the next frame.  If we are
		// on the first frame, we still need to store the UV and face information.
		// This will only go in the first frame, and will be referenced from the
		// remaining frames when we animate the model.

		// Check if we are past the first key frame
		if(i > 0)
		{
			// Here we add the current object (or frame) to our list object list
			m_pModel->pObject.push_back(currentFrame);
			continue;	// Go on to the next key frame
		}
	
		// We will only get here ONE because we just need this information
		// calculated for the first key frame.
			
		// Allocate memory for our UV coordinates and face information
		currentFrame.pTexVerts = new CV2 [currentFrame.numTexVertex];
		currentFrame.pFaces    = new tFace [currentFrame.numOfFaces];

		// Go through all of the uv coordinates and assign them over to our structure.
		// The UV coordinates are not normal uv coordinates, they have a pixel ratio of
		// 0 to 256.  We want it to be a 0 to 1 ratio, so we divide the u value by the
		// skin width and the v value by the skin height.  This gives us our 0 to 1 ratio.
		// For some reason also, the v coodinate is flipped upside down.  We just subtract
		// the v coordinate from 1 to remedy this problem.
		for(j=0; j < currentFrame.numTexVertex; j++)
		{
			currentFrame.pTexVerts[j].x = m_pTexCoords[j].u / float(m_Header.skinWidth);
			currentFrame.pTexVerts[j].y = 1 - m_pTexCoords[j].v / float(m_Header.skinHeight);
		}

		// Go through all of the face data and assign it over to OUR structure
		for(j=0; j < currentFrame.numOfFaces; j++)
		{
			// Assign the vertex indices to our face data
			currentFrame.pFaces[j].vertIndex[0] = m_pTriangles[j].vertexIndices[0];
			currentFrame.pFaces[j].vertIndex[1] = m_pTriangles[j].vertexIndices[1];
			currentFrame.pFaces[j].vertIndex[2] = m_pTriangles[j].vertexIndices[2];

			// Assign the texture coord indices to our face data
			currentFrame.pFaces[j].coordIndex[0] = m_pTriangles[j].textureIndices[0];
			currentFrame.pFaces[j].coordIndex[1] = m_pTriangles[j].textureIndices[1];
			currentFrame.pFaces[j].coordIndex[2] = m_pTriangles[j].textureIndices[2];
		}

		// Here we add the current object (or frame) to our list object list
		m_pModel->pObject.push_back(currentFrame);
	}
}

void CMD2::Draw(int nIndex)
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_CULL_FACE);	
	glCullFace(GL_FRONT);

	glBindTexture(GL_TEXTURE_2D, m_pTexture[m_pModel->pMaterials[0].texureId]);

	// Make sure we have valid objects just in case. (size() is in the vector class)
	if(m_pModel->pObject.size() <= 0) return;

	// Here we grab the current animation that we are on from our model's animation list
	tAnimationInfo *pAnim = &(m_pModel->pAnimations[nIndex]);

	// This gives us the current frame we are on.  We mod the current frame plus
	// 1 by the current animations end frame to make sure the next frame is valid.
	// If the next frame is past our end frame, then we go back to zero.  We check this next.
	int nextFrame = (m_pModel->currentFrame + 1) % pAnim->endFrame;

	// If the next frame is zero, that means that we need to start the animation over.
	// To do this, we set nextFrame to the starting frame of this animation.
	if(nextFrame == 0) 
		nextFrame =  pAnim->startFrame;

	// Get the current key frame we are on
	t3DObject *pFrame =		 &m_pModel->pObject[m_pModel->currentFrame];

	// Get the next key frame we are interpolating too
	t3DObject *pNextFrame =  &m_pModel->pObject[nextFrame];

	// Get the first key frame so we have an address to the texture and face information
	t3DObject *pFirstFrame = &m_pModel->pObject[0];

	// Next, we want to get the current time that we are interpolating by.  Remember,
	// if t = 0 then we are at the beginning of the animation, where if t = 1 we are at the end.
	// Anyhing from 0 to 1 can be thought of as a percentage from 0 to 100 percent complete.
	float t = ReturnCurrentTime(nextFrame);

	// Start rendering lines or triangles, depending on our current rendering mode (Lft Mouse Btn)
	glBegin(GL_TRIANGLES);

	// Go through all of the faces (polygons) of the current frame and draw them
	for(int j = 0; j < pFrame->numOfFaces; j++)
	{
		// Go through each corner of the triangle and draw it.
		for(int whichVertex = 0; whichVertex < 3; whichVertex++)
		{
			// Get the index for each point of the face
			int vertIndex = pFirstFrame->pFaces[j].vertIndex[whichVertex];

			// Get the index for each texture coordinate for this face
			int texIndex  = pFirstFrame->pFaces[j].coordIndex[whichVertex];

			// Make sure there was a UVW map applied to the object.  Notice that
			// we use the first frame to check if we have texture coordinates because
			// none of the other frames hold this information, just the first by design.
			if(pFirstFrame->pTexVerts) 
			{
				// Pass in the texture coordinate for this vertex
				glTexCoord2f(pFirstFrame->pTexVerts[ texIndex ].x, 
					pFirstFrame->pTexVerts[ texIndex ].y);
			}

			// Now we get to the interpolation part! (*Bites his nails*)
			// Below, we first store the vertex we are working on for the current
			// frame and the frame we are interpolating too.  Next, we use the
			// linear interpolation equation to smoothly transition from one
			// key frame to the next.

			// Store the current and next frame's vertex
			CV3 vPoint1 = pFrame->pVerts[ vertIndex ];
			CV3 vPoint2 = pNextFrame->pVerts[ vertIndex ];

			// By using the equation: p(t) = p0 + t(p1 - p0), with a time t
			// passed in, we create a new vertex that is closer to the next key frame.
			glVertex3f(vPoint1.x + t * (vPoint2.x - vPoint1.x), // Find the interpolated X
				vPoint1.y + t * (vPoint2.y - vPoint1.y), // Find the interpolated Y
				vPoint1.z + t * (vPoint2.z - vPoint1.z));// Find the interpolated Z
		}
	}
	glEnd();	
}

float CMD2::ReturnCurrentTime(int nextFrame)
{
	static float elapsedTime   = 0.0f;
	static float lastTime	  = 0.0f;

	// Get the current time in milliseconds
	float time = GetTickCount();

	// Find the time that has elapsed since the last time that was stored
	elapsedTime = time - lastTime;

	float t = elapsedTime / (1000.0f / kAnimationSpeed);

	// If our elapsed time goes over a 5th of a second, we start over and go to the next key frame
	if (elapsedTime >= (1000.0f / kAnimationSpeed) )
	{
		// Set our current frame to the next key frame (which could be the start of the anim)
		m_pModel->currentFrame = nextFrame;

		// Set our last time to the current time just like we would when getting our FPS.
		lastTime = time;
	}
	return t;
}

//////////////////////////////////////////////////////////////////////////

CMD2Mgr sMD2Mgr;
CMD2Mgr& CMD2Mgr::Inst()
{
	return sMD2Mgr;
}
DWORD CMD2Mgr::LoadMD2(const char* szPath)
{
	TiXmlDocument doc;  
	if (!doc.LoadFile(szPath))
	{  	
		assert(0);
		return false;
	}	

	TiXmlElement* DlgMgrElem = doc.RootElement();	
	for (TiXmlElement* DlgElem = DlgMgrElem->FirstChildElement(); DlgElem!=NULL; DlgElem = DlgElem->NextSiblingElement())
	{
		map<tstring, tstring> mapAtt;
		for(const TiXmlAttribute *pAtt  = DlgElem->FirstAttribute(); pAtt!=NULL; pAtt = pAtt->Next())
		{
			pair<map<tstring, tstring>::iterator, bool> ret;
			ret = mapAtt.insert(make_pair(conversion::UTF8ToUnicode(pAtt->Name()), conversion::UTF8ToUnicode(pAtt->Value())));
			if (ret.second == false)
				assert(0);
		}
		AddMD2Model(mapAtt);
	}
	return true;
}

void CMD2Mgr::AddMD2Model(map<tstring, tstring>& mapAtt)
{
	CMD2* pMD2 = new CMD2;
	DWORD dwID = DW_INVALID;
	map<tstring, tstring>::iterator iter;
	iter = mapAtt.find(_T("id"));
	if (iter == mapAtt.end()) assert(0);
	dwID = conversion::Unicode2Int(iter->second.c_str());

	iter = mapAtt.find(_T("path"));
	if (iter == mapAtt.end())assert(0);
	const wchar_t* path = iter->second.c_str();

	iter = mapAtt.find(_T("picpath"));
	if (iter == mapAtt.end())assert(0);
	
	pMD2->ImportMD2(path, iter->second.c_str());
	pair<map<DWORD, CMD2*>::iterator,bool> ret;
	ret = m_mapMD2.insert(make_pair(dwID, pMD2));
	if (ret.second == false) assert(0);
}

CMD2* CMD2Mgr::GetModel(DWORD dwID)
{
	map<DWORD, CMD2*>::iterator iter = m_mapMD2.find(dwID);
	if (iter != m_mapMD2.end())
	{
		return iter->second;
	}
	return NULL;
}
//////////////////////////////////////////////////////////////////////////